import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * 加载manager的工厂
 *
 * @author YooLin1c
 * @version 1.0.0
 * @since 2018-03-05 13:31
 */
public class ManagerFactory {
    /**
     * 记录热加载信息
     */
    private static final Map<String, LoadInfo> LOAD_TIME_MAP = new HashMap<>();
    /**
     * 要加载类的classpath
     */
    public static final String CLASS_PATH = ManagerFactory.class.getClassLoader().getResource("").getPath();
    /**
     * 需要热加载的类的全限定名
     */
    public static final String MY_MANAGER = "MyManagerImpl";

    /**
     * 获取热加载类
     *
     * @param className 热加载类的全限定名
     * @return 热加载类上级接口
     */
    public static BaseManager getManager(String className) {
        File loadFile = new File(CLASS_PATH.concat(className.replaceAll("\\.", "/")).concat(".class"));
        long lastModifiedTime = loadFile.lastModified();
        //如果没有加载该类或者时间戳变化则加载指定类
        if (LOAD_TIME_MAP.get(className) == null) {
            loadClass(className, lastModifiedTime);
        } else if (lastModifiedTime != LOAD_TIME_MAP.get(className).getLoadTime()) {
            loadClass(className, lastModifiedTime);
        }
        return LOAD_TIME_MAP.get(className).getManager();
    }

    /**
     * 1.使用自定义类加载器加载指定类
     * 2.创建BaseManager接口对象
     * 3.封装类的加载信息
     * 4.保存至Map记录类的热加载信息
     *
     * @param className        类的全限定名
     * @param lastModifiedTime 类文件的最后修改时间
     */
    private static void loadClass(String className, long lastModifiedTime) {
        MyClassLoader classLoader = new MyClassLoader(CLASS_PATH);
        Class<?> loadClass = classLoader.findClass(className);
        BaseManager manager = newInstance(loadClass);
        LoadInfo info = new LoadInfo(classLoader, lastModifiedTime);
        info.setManager(manager);
        LOAD_TIME_MAP.put(className, info);
    }

    /**
     * 反射创建BaseManager的实现类
     *
     * @param loadClass BaseManager的实现类
     * @return BaseManager的实现类对象
     */
    private static BaseManager newInstance(Class<?> loadClass) {
        try {
            return (BaseManager) loadClass.getConstructor(new Class[]{}).newInstance(new Object[]{});
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}
